<?php

    /**
     * @package      Barcode
     * @subpackage   Adapter
     *
     * @license      GNU Lesser General Public Licence see LICENCE-LGPL file or http://www.gnu.org/licenses/lgpl.html
     */
    class Barcode_Adapter_Code39 extends Barcode_Adapter_Abstract
    {

    ////////////////////////////////////////////////////////////////////////////
    //                            Attributes                                  //
    ////////////////////////////////////////////////////////////////////////////

        /**
         * Thin width
         *
         * @var     int
         */
        private $_barThinWidth = null;

        /**
         * Thick width
         *
         * @var     int
         */
        private $_barThickWidth = null;

    ////////////////////////////////////////////////////////////////////////////
    //                              Methods                                   //
    ////////////////////////////////////////////////////////////////////////////

        /**
         * Constructor
         */
        public function __construct()
        {
            /*
             * Defines path
             */
            $this->_path = Core_IO_Path::combine(
                                                    PROJECT_TEMP_PATH,
                                                    'barcodes',
                                                    'code39'
                                                );

            if (!Core_IO_Directory::exists($this->_path)) {
                Core_IO_Directory::createDirectory($this->_path);
            }

            /*
             * Bar width
             */
            $this->_barThinWidth    = $this->_barWidth;
            $this->_barThickWidth   = $this->_barThinWidth * 2;

            /*
             * Coding map
             */
            $this->_codingMap = array('0' => '000110100',
                                       '1' => '100100001',
                                       '2' => '001100001',
                                       '3' => '101100000',
                                       '4' => '000110001',
                                       '5' => '100110000',
                                       '6' => '001110000',
                                       '7' => '000100101',
                                       '8' => '100100100',
                                       '9' => '001100100',
                                       'A' => '100001001',
                                       'B' => '001001001',
                                       'C' => '101001000',
                                       'D' => '000011001',
                                       'E' => '100011000',
                                       'F' => '001011000',
                                       'G' => '000001101',
                                       'H' => '100001100',
                                       'I' => '001001100',
                                       'J' => '000011100',
                                       'K' => '100000011',
                                       'L' => '001000011',
                                       'M' => '101000010',
                                       'N' => '000010011',
                                       'O' => '100010010',
                                       'P' => '001010010',
                                       'Q' => '000000111',
                                       'R' => '100000110',
                                       'S' => '001000110',
                                       'T' => '000010110',
                                       'U' => '110000001',
                                       'V' => '011000001',
                                       'W' => '111000000',
                                       'X' => '010010001',
                                       'Y' => '110010000',
                                       'Z' => '011010000',
                                       '-' => '010000101',
                                       '*' => '010010100',
                                       '+' => '010001010',
                                       '$' => '010101000',
                                       '%' => '000101010',
                                       '/' => '010100010',
                                       '.' => '110000100',
                                       ' ' => '011000100');
        }

        /**
         * Creates the barcode image.
         *
         * @param       int                 $value
         * @throws      Core_Exception
         * @return      string              Image path
         */
        public function process($value)
        {
            $value = strtoupper($value);

            /*
             *  Check barecode value
             */
            if (preg_match('/[^0-9A-Z\-*+\$%\/. ]/', $value)) {
                throw new Core_Exception('Fatal error : Invalid characters for code39 barcode');
            }

            $cacheFile = Core_IO_Path::combine($this->_path, $value . '.png');

            if (Core_IO_File::exists($cacheFile)) {
                // If barcode is still in cache return its path
                return $cacheFile;
            }

            $value      = '*' . $value . '*';
            $barcode    = '';

            foreach (str_split($value) as $character) {
                $barcode .= $this->_dumpCode( $this->_codingMap[$character] . '0');
            }

            $barcode_len = strlen($barcode);

            /*
             * Create GD image object
             */
            $img = ImageCreate($barcode_len, $this->_barcodeHeight);

            /*
             * Allocate black and white colors to the image
             */
            $black      = ImageColorAllocate($img, 0, 0, 0);
            $white      = ImageColorAllocate($img, 255, 255, 255);
            $fontHeight = ImageFontHeight($this->_fontSize);
            $fontWidth  = ImageFontWidth($this->_fontSize);

            /*
             * Fill background with white color
             */
            ImageFill( $img, 0, 0, $white );

            /*
             * Initialize X position
             */
            $xpos = 0;

            /*
             * Draw barcode bars to image
             */
            foreach (str_split($barcode) as $character_code ) {
                if ($character_code == 0) {
                    ImageLine($img, $xpos, 0, $xpos, $this->_barcodeHeight - $fontHeight - 1, $white);
                } else {
                    ImageLine($img, $xpos, 0, $xpos, $this->_barcodeHeight - $fontHeight - 1, $black);
                }

                $xpos++;
            }

            /*
             * Draw text under barcode
             */
            ImageString($img,
                        $this->_fontSize,
                        ($barcode_len - $fontWidth * strlen($value)) / 2,
                        $this->_barcodeHeight - $fontHeight,
                        $value,
                        $black);

            ImagePNG($img,$cacheFile);
            ImageDestroy($img);

            return $cacheFile;
        }

        /**
         * Dump code
         *
         * @param       string      $code
         * @return      string
         */
        private function _dumpCode($code)
        {
            $result     = '';
            $color      = 1; // 1: Black, 0: White

            // if $bit is 1, line is wide; if $bit is 0 line is thin
            foreach (str_split( $code ) as $bit) {
                $result    .= ( ( $bit == 1 ) ? str_repeat( "$color", $this->_barThickWidth ) : str_repeat( "$color", $this->_barThinWidth ) );
                $color      = ( ( $color == 0 ) ? 1 : 0 );
            }

            return $result;
        }
    }